home *** CD-ROM | disk | FTP | other *** search
- /*
- printf formatter for LightspeedC
-
- (C) Copyright 1985, 1986. THINK Technologies, Inc. All rights reserved.
-
- pdg - 6/11/86 - revised
- */
-
- /* comment this out if you don't need floating point numeric support */
- #define _FORMAT_FP_
-
- #ifndef _stdioh_
- #include "stdio.h"
- #endif
-
- #ifndef _MacTypes_
- #include "MacTypes.h"
- #endif
-
- #ifndef _saneh_
- #include "sane.h"
- #endif
-
- #ifndef _setjmph_
- #include "setjmp.h"
- #endif
-
- #ifndef _math_
- #include "Math.h"
- #endif
-
- #ifdef _MC68881_
- /* conversion from 68881 to SANE extended type */
- static void x96tox80(x96, x80)
- register Extended96 *x96;
- register Extended80 *x80;
-
- {
- (*x80).exponent = (*x96).exponent;
- (*x80).mantissa = (*x96).mantissa;
- }
-
- #define _tox80() x96tox80(doublep, &x80);
- #define _ToDecimal() fp68k(&_decform_, &x80, &_decimal_, FFEXT+FOB2D)
-
- #else
-
- #define _tox80()
- #define _ToDecimal() fp68k(&_decform_, doublep, &_decimal_, FFEXT+FOB2D)
-
- #endif
-
- /* it turns out that defining min, max, and isdigit as follows works pretty
- well and drags in less code from other modules */
- #undef min
- #undef max
- #undef isdigit
-
- #define min(a,b) ( (a<b) ? (a) : (b) )
- #define max(a,b) ( (a>b) ? (a) : (b) )
- #define isdigit(c) ( (c<='9') && (c>='0') )
-
- #define BLANKS 0
-
- void (*_output)(); /* global pointer to function for output */
- int _num_count; /* number of characters transmitted */
- jmp_buf env; /* global error return mechanism */
-
- static char _hex_case; /* used for x X case */
- static char fillchar; /* space or '0' depending on mode */
- static int base; /* global to store base under conversion */
-
- #line 0 check_for_three()
- static char *check_for_three(ptr,tptr)
- char *ptr,*tptr;
- {
- int l;
-
- /* if length of exponent is 3 or more then return */
-
- if ((l =len_of_str(ptr))>2) return(tptr);
-
- /* This implementation normally makes the exponent 3 bytes wide */
-
- *tptr++ = '0'; /* make default width = 2 */
-
- /* if 2 byte default width is desired then comment out the
- following line of code */
-
- if (l == 1) *tptr++ = '0'; /* make default width = 3 */
-
- return(tptr);
- }
-
- #line 0 dumpbuffer()
- static void dumpbuffer(tbufptr) /* write buffer to output stream */
- register char *tbufptr;
- {
- register void (*outputx)() = _output;
-
- if (*tbufptr == '@') tbufptr++;
-
- while (*tbufptr)
- (*outputx)(*tbufptr++);
-
- }
-
- #line 0 dopadding()
- static void dopadding(tempbuffer,left_justify,zero_fill,width)
- register char *tempbuffer;
- register Boolean left_justify,zero_fill;
- register int width;
- {
- register int length;
- register void (*outputx)() = _output;
-
- if ((length = len_of_str(tempbuffer))<width)
- {
- width -= length;
- if (left_justify == false)
- {
- if ((*tempbuffer == ' ')||
- (((*tempbuffer == '+')||(*tempbuffer == '-'))&&zero_fill) )
- {
- (*outputx)(*tempbuffer);
-
- *tempbuffer = '@';
- }
-
- while (--width >= 0)
- (*outputx)(zero_fill?'0':' ');
-
- dumpbuffer(tempbuffer);
- }
- else
- {
- dumpbuffer(tempbuffer);
-
- while (--width >= 0)
- (*outputx)(' ');
- }
- }
- else /* pdg - 7/23/86 missing else! */
- dumpbuffer(tempbuffer);
- }
-
- /* pdg - 6/10/86 - revise for efficiency */
-
- #line 0 len_of_str()
- static int len_of_str(s)
- register char *s;
- {
- register char *s0 = s;
-
- while (*s) s++;
-
- return(s-s0);
- }
-
- #line 0 padd()
- static void padd(zero_fill,count)
- Boolean zero_fill;
- register int count;
- {
- while (--count >= 0)
- (*_output)(((zero_fill)?'0':' '));
- }
-
-
- #line 0 output_number()
- static void output_number(value)
- unsigned long value;
- {
- unsigned long quotient;
- register int remainder;
-
- if ((quotient = value / base) != 0) output_number(quotient);
-
- remainder = (value % base);
-
- (*_output)(((remainder<10) ? remainder + '0' :
- remainder + _hex_case));
- }
-
- #line 0 length of number()
- static int length_of_number(number)
- register unsigned long number;
- {
- register int places = 1;
-
- while (number /= base) places++;
-
- return (places);
- }
-
- #ifdef _FORMAT_FP_
-
- char *PtoCstr(); /* need proper typing of return value */
-
- #line 0 cvtf2string()
- static char *cvtf2string(doublep, style, digits, floatbufferp)
- double *doublep;
- char style;
- short digits;
- char *floatbufferp;
- {
- DecForm _decform_; /* information record for conversion */
- Decimal _decimal_; /* intermediate record */
-
- #ifdef _MC68881_
- Extended80 x80;
- #endif
-
- /* convert double to Extended80 (as defined in Math881.h) */
- _tox80();
-
- /* convert binary to decimal record */
-
- _decform_.style = style;
- _decform_.digits = digits;
-
- _ToDecimal();
-
- /* decimal record to string */
-
- _decform_.style= style;
- _decform_.digits = digits;
-
- Dec2Str(_decform_, &_decimal_, floatbufferp);
-
- /* convert to C string from Pascal string used by SANE */
-
- return (PtoCstr(floatbufferp));
- }
-
- #endif _FORMAT_FP_
-
-
- #line 0 _format()
- /*----------------------------------------------------------------------------
- "_format" is the internal routine to do the conversion process. This routine
- is passed a pointer to the pointer to the format string. By scanning the
- format string until a format code is encountered, information can be extrac-
- ted to determine the what argument was present on the stack and processing
- can be handled. Note that the following stack layout applies after the call
- to _format (standard C calling conventions):
-
- HI memory
- |---------------------|
- | last argument | last argument for format
- |---------------------|
- .
- .
- |---------------------|
- |--->---| first argument | points to format string
- | |---------------------|
- | | return address |
- | |---------------------|
- |---<---| argument to _format | points to pointer to format
- |---------------------|
- | return address |
- |---------------------|
- LO memory
-
- The _format routine sets "format" to point to the format string and scans
- over it. By incrementing the parameter "ptr" (passed to _format) the
- first argument for substitution in the format string can be accessed.
- How the arguments are interpreted is derived from the format string.
- A pointer to a function "*_output" is used to stuff processed characters
- to their destination.
-
- -----------------------------------------------------------------------------*/
- int _format(fmt,var_stuff)
- char **fmt; /* pointer to pointer to format string */
- Boolean var_stuff;
- {
- Boolean left_justify; /* flag to indicate left justiccation */
- Boolean do_precision; /* flag limited precision */
- Boolean its_a_long; /* flag to indicate long integer */
- Boolean zero_fill; /* flag to indicate padding with zero */
- Boolean sign_on; /* flag to indicate '+' */
- Boolean space_on; /* flag to indicate ' ' */
- Boolean pound_on; /* flag to indicate '#' */
- Boolean neg; /* flag to indicate a negative number */
-
- register char *format; /* pointer to format string */
- register char *argument; /* pointer to arguments for fmt string */
-
- #ifdef _FORMAT_FP
- #define outputx _output
- #else
- register void (*outputx)() = _output;
- #endif
-
- register char c; /* current character under inspection */
- unsigned long value; /* for conversion of numerics */
- register int length; /* length of argument to be inserted */
- register int width; /* field width as specified by format */
- register int numleft; /* # of characters left to print */
- register int precision; /* precision of number to be converted */
-
- #ifdef _FORMAT_FP_
-
- /* floating point junk */
- double tempdouble;
- char floatbuffer[256];
- char tempbuffer[256];
- register char *bufptr;
- register char *tbufptr;
-
- #endif _FORMAT_FP_
-
- /* set up the direct error return to caller */
-
- if (setjmp(env)) return (EOF);
-
- _num_count = 0; /* set # characters transmitted to zero */
-
- format = *fmt++; /* set "format" to the format string */
- /* "fmt" is left pointing to first argument */
- argument = (char *) fmt; /* set "argument" to point to first argument */
-
- /* if variable, indirect through first argument to find real arg list */
-
- if (var_stuff) argument = (char *)(*(long *)argument);
-
- while (c = *format++) /* scan format string until NULL */
- {
- if (c == '%')
- {
- left_justify = false;
- zero_fill = false;
- sign_on = false;
- space_on = false;
- pound_on = false;
- neg = false;
- flagloop:
- switch(c = *format++)
- {
- case '-': left_justify = true;
- zero_fill = false;
- goto flagloop;
-
- case '0': zero_fill = (!left_justify);
- goto flagloop;
-
- case '+': sign_on = true;
- space_on = false;
- goto flagloop;
-
- case ' ': space_on = (!sign_on);
- goto flagloop;
-
- case '#': pound_on = true;
- goto flagloop;
-
- case '*': numleft = width = *(int *)argument;
- argument += sizeof(int);
- goto getdot;
-
- default: format--; /* need to back up if unrecognized */
- break;
- }
-
-
- /* compute total field width */
-
- {char *tempformat = format; /* let format be register */
-
- numleft = width = (isdigit(*format)) ? _std_decode(&tempformat) : 0;
- format = tempformat;
- }
-
- getdot:
- /* compute precision within total field width */
-
- precision = 0;
-
- if (do_precision = (*format == '.'))
- {
- if (*++format == '*')
- {
- precision = *(int *)argument;
- argument += sizeof(int);
- format++;
- }
- else
- {char *tempformat = format; /* let format be register */
-
- precision = _std_decode(&tempformat);
- format = tempformat;
- }
- }
-
- if (*format == '%')
- {
- if (left_justify)
- {
- (*outputx)('%');
- padd(BLANKS,width-1);
- }
- else
- {
- padd(zero_fill,width-1);
- (*outputx)('%');
- }
- format++;
- continue;
- }
-
- /* check if long integer is required */
-
- if (its_a_long = ((c = (*format++)) == 'l'))
- c = *format++;
-
- /* check for short %h.. */
-
- if (c == 'h') c = *format++;
-
- /* note that "format" is left ready for the next pass */
- switch (c)
- {
-
- case 'd':
- {register long svaluel; /* for conversion of signed long */
- register int svaluei; /* for conversion of signed int */
-
- if (its_a_long)
- {
- svaluel = *(long *)argument;
- argument += sizeof(long);
-
- value = (unsigned long)svaluel;
-
- if (svaluel < 0) goto negate;
- }
- else
- {
- svaluei = *(int *)argument;
- argument += sizeof(int);
-
- value = (unsigned long)svaluei;
-
- if (svaluei < 0)
- {
- negate: neg = true;
- value = -value;
- }
- }
-
- /* fall into case 'u' */
- }
-
- case 'u':
- base = 10;
- goto evaluate;
-
- case 'o':
- base = 8;
- goto evaluate;
-
- case 'x': _hex_case = 'a'-10;
- goto sethex;
- case 'X':
- _hex_case = 'A'-10;
- sethex:
- base = 16;
- evaluate:
- /* get the target value in a long,
- readjust the argument pointer */
-
- if (c != 'd')
- {
- if (its_a_long)
- {
- value = *(unsigned long *)argument;
- argument += sizeof (unsigned long);
- }
- else
- {
- value = *(unsigned *)argument;
- argument += sizeof (unsigned);
- }
- }
-
- /* get the length of the output string */
-
- length = length_of_number(value);
-
- /* now figure out the positioning */
-
- if (zero_fill)
- {
- if (((neg)||(sign_on)||
- (space_on)) && (c =='d'))
- {
- if (neg)
- {
- (*outputx)('-');
- }
- else
- (*outputx)((sign_on ? '+' : ' '));
-
- numleft--; /* room for sign */
- }
-
- if (((c=='o')||(c == 'X')||(c == 'x'))&&(pound_on))
- {
- (*outputx)('0');
- numleft--;
-
- if (c == 'X') { (*outputx)('X');numleft--; }
- if (c == 'x') { (*outputx)('x');numleft--; }
- }
-
- padd(zero_fill, numleft - max(length,precision));
-
- if (precision > length)
- padd(zero_fill,precision-length);
-
- output_number(value);
-
- }
-
- if (left_justify)
- {
- if (c =='d')
- {
- if (neg)
- {
- (*outputx)('-');
- numleft--;
- }
- else
- if((sign_on)||(space_on))
- {
- (*outputx)((sign_on ? '+' : ' '));
-
- numleft--;
- }
- }
-
- if (((c=='o')||(c == 'X')||(c == 'x'))&&(pound_on))
- {
- (*outputx)('0');
-
- numleft--;
-
- if (c == 'X') { (*outputx)('X');numleft--; }
- if (c == 'x') { (*outputx)('x');numleft--; }
- }
-
- padd(true,precision-length);
-
- if(precision-length>0) numleft -= (precision-length);
-
- output_number(value);
-
- numleft -= length;
-
- padd(BLANKS, numleft);
-
- }
-
- if ((!zero_fill)&&(!left_justify))
- {
- if (((neg)||(space_on)||
- (sign_on)) && (c == 'd'))
- {
- padd(zero_fill, width - max(length,precision)-1);
-
- if (neg)
- {
- (*outputx)('-');
- }
- else
- (*outputx)(((sign_on)?'+':' '));
-
- padd(true,precision-length);
-
- output_number(value);
-
- } /* normal decimal output with sign */
- else
- if ((pound_on)&&((c == 'o')||(c == 'X')||(c == 'x')))
- {
- numleft = 2;
- if ( c == 'o') numleft = 1;
- padd(zero_fill, width - max(length,precision)-numleft);
-
- (*outputx)('0');
-
- if (c == 'X') (*outputx)('X');
- if (c == 'x') (*outputx)('x');
-
- padd(true,precision-length);
-
- output_number(value);
- }
- else
- {
- padd(zero_fill, width - max(precision,length));
- padd(!zero_fill,precision-length);
- output_number(value);
- }
- } /* ((!zero_fill)&&(!left_justify)) */
- break;
-
- case 'c':
- /* get the character and readjust the pointer
- note that a char on the stack is expanded to int */
-
- if (left_justify)
- {
- (*outputx)(*(int *) argument);
- padd(BLANKS, width - 1);
- }
- else
- {
- padd(zero_fill, width - 1);
- (*outputx)(*(int *) argument);
- }
- argument += sizeof (int);
- break;
-
- case 'p':
- {register char *char_ptr;
-
- /* get the length of the string */
- char_ptr = *((char **)argument);
- length = *char_ptr++;
- argument += sizeof(char_ptr);
-
- /* check for truncation */
- if (do_precision)
- if (length > precision) length = precision;
-
- /* set width to padding width */
- if ((width -= length) < 0) width = 0;
-
- if (left_justify)
- {
- while (--length >= 0)
- (*outputx)(*char_ptr++);
-
- padd(BLANKS, width);
- }
- else
- {
- padd(zero_fill, width);
- while (--length >= 0)
- (*outputx)(*char_ptr++);
- }
- break;
- }
-
- case 's':
- {register char *char_ptr;
-
- /* get the length of the string */
-
- length = len_of_str(char_ptr = *((char **) argument));
- argument += sizeof(char_ptr);
-
- /* check for truncation */
- if (do_precision)
- if (length > precision) length = precision;
-
- /* set width to padding width */
- if ((width -= length) < 0) width = 0;
-
- if (left_justify)
- {
- while (--length >= 0)
- (*outputx)(*char_ptr++);
-
- padd(BLANKS, width);
- }
- else
- {
- padd(zero_fill, width);
- while (--length >= 0)
- (*outputx)(*char_ptr++);
- }
- break;
- }
-
- #ifdef _FORMAT_FP_
-
- case 'f':
- tempdouble = *((double *)argument); /* consume argument */
- argument += sizeof(double);
-
-
- /* default precision is 6 */
- if (do_precision == false) precision = 6;
- regf:
- bufptr = cvtf2string(&tempdouble, 1, precision, floatbuffer);
- nregf:
- tbufptr = tempbuffer;
-
- if ((tempdouble >= 0)&&((space_on)||
- (sign_on)))
- *tbufptr++ = (sign_on ? '+' : ' ');
-
- while(c = *(bufptr++))
- *tbufptr++ = c;
-
- if ((pound_on)&&(precision == 0))
- *tbufptr++ = '.';
-
- *tbufptr = '\0';
-
- dopadding(tempbuffer,left_justify,zero_fill,width);
-
- break;
-
- case 'e':
- case 'E':
- tempdouble = *((double *)argument); /* consume argument */
- argument += sizeof(double);
-
- /* default precision is 6 */
- if (do_precision == false) precision = 6;
-
- bufptr = cvtf2string(&tempdouble, 0, precision+1, floatbuffer);
-
- rege:
- tbufptr = tempbuffer;
-
- if ((tempdouble >= 0)&&(sign_on)) /* if + then make prefix */
- *bufptr = '+';
-
- if ((space_on == false)&&(*bufptr == ' '))
- bufptr++; /* if leading space and no flag fix it */
-
- while(*(bufptr))
- {
- if (*bufptr == 'e') /* is this the e char? */
- {
- neg = true;
- *bufptr = c; /* convert if if necessary */
- if ((pound_on)&&(precision == 0))
- *tbufptr++ = '.';
- }
- if ((*(bufptr-2) == c)&&(neg)) /* should we pad the exp */
- tbufptr = check_for_three(bufptr,tbufptr);
- if (*bufptr != '@')
- *tbufptr++ = *bufptr++;
- }
- *tbufptr = '\0';
-
- dopadding(tempbuffer,left_justify,zero_fill,width);
-
- break;
-
- case 'g':
- case 'G':
- tempdouble = *((double *)argument); /* consume argument */
- argument += sizeof(double);
-
- if (precision < 1) precision = 1;
-
- if (do_precision == false) precision = 6;
-
-
- /* convert to e form */
-
- bufptr = cvtf2string(&tempdouble, 0, precision, floatbuffer);
-
- /* look at char after 'e' */
-
- while (*bufptr++ != 'e')
- ;
- neg = (*bufptr++ == '-'); /* is it a negative exponent */
- tbufptr = bufptr;
-
- /* get size of exponent */
-
- length = _std_decode(&tbufptr);
- if (neg) length *= -1;
-
- if (pound_on) /* if # flag is on and ... */
- {
- if ((length<=precision)&&(length>=-4))
- goto regf; /* do regular f routine */
- else
- {
- /* this gives precision-1 digits after
- . instead of precision */
-
- bufptr = (char *)floatbuffer;
- c -= 2;
- goto rege; /* do regular e routine */
- }
- }
-
-
- if ((length<=precision)&&(length>=-4))
- {register Boolean strip_it = false;
-
- /* convert to f format */
-
- bufptr = cvtf2string(&tempdouble, 1, precision-length, floatbuffer);
-
- /* don't strip string unless therre is a . */
- while (*bufptr)
- {
- strip_it = strip_it?true:(*bufptr == '.');
- bufptr++;
- }
- bufptr -= 1; /* was 2 */
-
- /* strip trailing zeros */
- if (strip_it)
- {
- while (*bufptr == '0') bufptr--;
- if (*bufptr == '.') bufptr--;
- *(++bufptr) = '\0';
- }
-
- bufptr = (char *)floatbuffer;
-
- /* now test for \0 or -\0 and adjust */
- if ((*bufptr == '\0') ||
- ((*bufptr=='-')&&(*(bufptr+1)=='\0')))
- {
- *bufptr = '0';
- *(bufptr+1) = '\0';
- }
-
- goto nregf;
-
- break;
- }
-
-
- /* back up before the e and strip trailing zeros */
-
- bufptr -= 3;
-
- while ((*bufptr == '0')||(*bufptr == '.'))
- *(bufptr--) = '@';
-
- /* bufptr = (char *)floatbuffer;
- c -= 2; / convert form g to e, G to E /
- goto rege;
- */
-
- if ((space_on == false)&&(floatbuffer[0] == ' '))
- floatbuffer[0] = '@';
-
- if ((tempdouble>=0) && (sign_on)) floatbuffer[0] = '+';
-
- tbufptr = (char *)tempbuffer;
- bufptr = (char *)floatbuffer;
-
- while (*bufptr != 'e')
- if (*bufptr != '@')
- *tbufptr++ = *bufptr++;
- else
- bufptr++;
-
- *bufptr = c-2; /* convert G to E, g to e */
- *tbufptr++ = *bufptr++;
- *tbufptr++ = *bufptr++;
- tbufptr = check_for_three(bufptr,tbufptr);
- while (*bufptr)
- *tbufptr++ = *bufptr++;
- *tbufptr = '\0';
- dopadding(tempbuffer,left_justify,zero_fill,width);
-
- break;
- #endif _FORMAT_FP_
-
- default:
- /* input garbled, bark: arf arf */
- return (EOF);
-
- } /* end of switch */
- } /* end of if */
- else
- /* copy it directly to output */
-
- (*outputx)(c);
-
- } /* end of while */
-
- return(_num_count);
-
- (void) std_ver(); /* make the linker drag it in */
- }
-